package org.xyz.service;

import cn.hutool.core.lang.UUID;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.xyz.dao.ProductMapper;
import org.xyz.dao.SettingsMapper;
import org.xyz.dao.UserMapper;
import org.xyz.entity.*;
import org.xyz.util.WXPayUtil;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.wechat.pay.java.core.http.Constant.*;

/**
 * @description: 微信支付
 * @link: https://blog.csdn.net/m0_61470934/article/details/132832522
 * @create: 2024/1/20 14:28
 **/
@Component
@Slf4j
public class WxPayService {

    private RSAAutoCertificateConfig notificationConfig;

    @Autowired
    SettingsMapper settingsMapper;

    @Autowired
    UserMapper userMapper;

//    @Autowired
//    private WxPayV3Bean wxPayV3Bean;

    @Value("${wx.session_key_url}")
    private String sessionKeyUrl;

    /**
     * 商户号
     */
    @Value("${wx.mch_id}")
    public String merchantId;

    /**
     * 商户API私钥路径
     */
    @Value("${wx.private_key_path}")
    public String privateKeyPath;

    /**
     * 商户证书序列号
     */
    @Value("${wx.serial_number}")
    public String merchantSerialNumber;

    /**
     * 商户APIV3密钥
     */
    @Value("${wx.api_V3_key}")
    public String apiV3Key;

    /**
     * 通知地址
     */
    @Value("${wx.notify_url}")
    public String notifyUrl;

    /**
     * 小程序ID
     */
    @Value("${wx.app_id}")
    public String wxAppid;


    /**
     * 拉起微信小程序预付款信息
     *
     * @param orderNo    订单编号
     * @param totalPrice 支付的价格（字符串例如：25.66）
     * @param openId     微信openId
     * @return 预支付ID和openId的map
     */
    public Map<String, Object> wxSmartPay(String orderNo, String totalPrice, String openId) {
        Map<String, Object> map = new HashMap<>();
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(notificationConfig).build();
        PrepayRequest request = new PrepayRequest();
        request.setAppid(wxAppid);
        request.setMchid(merchantId);
        request.setDescription("商品描述");
        request.setOutTradeNo(orderNo);
        request.setNotifyUrl(notifyUrl);
        Amount amount = new Amount();
        amount.setTotal(convertToCents(totalPrice));
        amount.setCurrency("CNY");
        request.setAmount(amount);
        Payer payer = new Payer();
        payer.setOpenid(openId);
        request.setPayer(payer);
        log.info("小程序调用微信支付请求参数：{}", JSON.toJSONString(request));
        PrepayWithRequestPaymentResponse requestPaymentResponse = service.prepayWithRequestPayment(request);
        log.info("小程序调用微信支付响应结果：{}", JSON.toJSONString(requestPaymentResponse));
        // requestPaymentResponse对象中包含小程序中拉起微信支付的参数
        map.put("data", requestPaymentResponse);
        map.put("openId", openId);
        return map;
    }

    public WxPayRespVO createOrder(WXPayOrderReqVO req) throws Exception {
        try {
            // 使用自动更新平台证书的RSA配置
            // 一个商户号只能初始化一个配置，否则会因为重复的下载任务报错
            Config config =
                    new RSAAutoCertificateConfig.Builder()
                            .merchantId(merchantId)
                            .privateKeyFromPath(privateKeyPath)
                            .merchantSerialNumber(merchantSerialNumber)
                            .apiV3Key(apiV3Key)
                            .build();
            // 构建service
            JsapiService service = new JsapiService.Builder().config(config).build();
            // request.setXxx(val)设置所需参数，具体参数可见Request定义
            PrepayRequest request = new PrepayRequest();

            request.setAppid(wxAppid);
            request.setMchid(merchantId);
            //request.setDescription(req.getGoodsName());
            request.setNotifyUrl(notifyUrl);
            request.setOutTradeNo(req.getOrderSn());

            // 记录：谁（手机号），买了什么（productType），以什么价格（price），买了多少（count），付了多少钱（total）；
            Integer type_product = req.getOrderType();
            if (type_product == 1) {
                request.setDescription("MonthlyProd");
            } else if (type_product == 2) {
                request.setDescription("YearlyProd");
            } else {
                request.setDescription("TimelyProd");
            }
            //Product product = productMapper.selectOne(new LambdaQueryWrapper<Product>().eq(Product::getType, type_product));
            Float price = 0f;
            Map<Integer, String> priceMap = new HashMap<>(3);
            priceMap.put(1, "price_month");
            priceMap.put(2, "price_year");
            priceMap.put(3, "price_trial");
            Settings settings = settingsMapper.selectOne(new LambdaQueryWrapper<Settings>().eq(Settings::getSetting, priceMap.get(type_product)));
            price = Float.parseFloat(settings.getValue());

            String who_phone = req.getOrderSn().split("_")[0];
            Long phone = Long.parseLong(who_phone);
            Integer count = req.getOrderCount();
//            Integer price = product.getPrice();
            Float total = price * count * 100;

            log.info("count="+count+", price="+price+", total="+total);

            Map<String, Object> attaches = new HashMap<>();
            attaches.put("phone", phone);
            attaches.put("price", price);
            attaches.put("total", total);
            attaches.put("count", count);
            attaches.put("type", req.getOrderType());

            Amount amount = new Amount();
            // 查询价格（这里应该是：价格*数量）
            amount.setTotal(Math.round(total)); //req.getTotalPrice()
            request.setAmount(amount);

            request.setAttach(JSONUtil.toJsonStr(attaches));
            Payer payer = new Payer();
            payer.setOpenid(WXPayUtil.getOpenId(req.getWxCode()));
            request.setPayer(payer);
            // 调用下单方法，得到应答
            PrepayResponse response = service.prepay(request);
            WxPayRespVO vo = new WxPayRespVO();
            String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
            vo.setTimeStamp(timeStamp);
            String substring = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
            vo.setNonceStr(substring);
            String signatureStr = Stream.of(wxAppid, String.valueOf(timeStamp), substring, "prepay_id=" + response.getPrepayId())
                    .collect(Collectors.joining("\n", "", "\n"));
            String sign = WXPayUtil.getSign(signatureStr, privateKeyPath);
            vo.setPaySign(sign);
            vo.setPrepayId("prepay_id=" + response.getPrepayId());
            return vo;
        }catch (ServiceException e){
            JSONObject parse = JSONObject.parseObject(e.getResponseBody());
            throw new Exception(parse.getString("message"));
//            throw new ResultException(ResultEnum.ERROR,parse.getString("message"));
        }catch (Exception e){
            throw new Exception(e.getMessage());
//            throw new ResultException(ResultEnum.ERROR,e.toString());
        }
    }

    public void payNotify(HttpServletRequest request) {
        try {
            //读取请求体的信息
            ServletInputStream inputStream = request.getInputStream();
            StringBuffer stringBuffer = new StringBuffer();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String s;
            //读取回调请求体
            while ((s = bufferedReader.readLine()) != null) {
                stringBuffer.append(s);
            }
            String s1 = stringBuffer.toString();
            String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
            String nonce = request.getHeader(WECHAT_PAY_NONCE);
            String signType = request.getHeader("Wechatpay-Signature-Type");
            String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
            String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
            // 如果已经初始化了 RSAAutoCertificateConfig，可直接使用
            // 没有的话，则构造一个
            //log.error(com.alibaba.fastjson2.JSON.toJSONString(wxPayV3Bean));
            NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(merchantId)
                    .privateKeyFromPath(privateKeyPath)
                    .merchantSerialNumber(merchantSerialNumber)
                    .apiV3Key(apiV3Key)
                    .build();
            // 初始化 NotificationParser
            NotificationParser parser = new NotificationParser(config);
            RequestParam requestParam=new RequestParam.Builder()
                    .serialNumber(serialNo)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    // 若未设置signType，默认值为 WECHATPAY2-SHA256-RSA2048
                    .signType(signType)
                    .body(s1)
                    .build();
            Transaction parse = parser.parse(requestParam, Transaction.class);
            System.out.println("parse = " + parse);
        } catch (Exception e) {
//            throw new ResultException(ResultEnum.ERROR,e.toString());
//            throw new Exception(e.getMessage());
            e.printStackTrace();
        }
    }

    // https://blog.csdn.net/weixin_61428498/article/details/134728436
    public User queryPayOrder(String tradeNo) {
        // 使用自动更新平台证书的RSA配置，配置微信支付的自动证书管理功能
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(merchantId)
                        .privateKeyFromPath(privateKeyPath)
                        .merchantSerialNumber(merchantSerialNumber)
                        .apiV3Key(apiV3Key)
                        .build();
        // 构建service,用于处理JSAPI支付相关的操作
        JsapiService service = new JsapiService.Builder().config(config).build();

        //根据商户订单号查询支付订单
        QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
        queryRequest.setMchid(merchantId);
        queryRequest.setOutTradeNo(tradeNo);
        com.wechat.pay.java.service.payments.model.Transaction transaction = service.queryOrderByOutTradeNo(queryRequest);
        log.info("-----------------------支付状态：" + transaction.getTradeState());
        // String state = transaction.getTradeState().name();
        return updateAccount(transaction);
    }

    private User updateAccount(com.wechat.pay.java.service.payments.model.Transaction transaction) {
        log.info(transaction.toString());
        String attach = transaction.getAttach();
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            JsonNode jsonNode = objectMapper.readTree(attach);
            int total = jsonNode.path("total").asInt();
            int price = jsonNode.path("price").asInt();
            int count = jsonNode.path("count").asInt();
            int type = jsonNode.path("type").asInt();
            long phone = jsonNode.path("phone").asLong();
            User selectedUser = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));
            if (type == 3) {
                selectedUser.setLeftTrials(count + selectedUser.getLeftTrials());
            } else if (type == 2){
                selectedUser.setLeftDays(count* 365 + selectedUser.getLeftDays());
            } else if (type == 1) {
                selectedUser.setLeftDays(count* 30 + selectedUser.getLeftDays());
            }
            int rel = userMapper.updateById(selectedUser);
            if (rel > 0) {
                return selectedUser;
            }
            return  null;
        } catch (Exception e) { ; }
        return null;
    }

    /**
     * 接受微信通知
     *
     * @param requestParam 微信服务器请求过来的参数
     * @return 解密之后的参数对象
     */
    public Transaction wxNotify(RequestParam requestParam) {
        NotificationParser parser = new NotificationParser(notificationConfig);
        Transaction transaction;
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            transaction = parser.parse(requestParam, Transaction.class);
        } catch (Exception e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.info("微信通知，签名校验失败", e);
            return null;
        }
        log.info("微信通知，解密结果：{}", JSON.toJSONString(transaction));
        return transaction;
    }


    /**
     * 转换价格
     *
     * @param totalPrice
     * @return
     */
    public static Integer convertToCents(String totalPrice) {
        BigDecimal bigDecimal = new BigDecimal(totalPrice);
        BigDecimal multipliedBy100 = bigDecimal.multiply(new BigDecimal(100));
        return multipliedBy100.setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
    }

    /**
     * 获取微信openId
     *
     * @param code
     * @return
     */
    public String getOpenId(String code) {
        try {
            String res = HttpUtil.get(String.format(sessionKeyUrl, code));
            log.debug("获取WX_Session_Key结束，结果：{}", res);
            JSONObject jsonObject = JSON.parseObject(res);
            if (jsonObject.containsKey("openid")) return jsonObject.get("openid").toString();
            else return "";
        } catch (Exception e) {
            log.error("wxCode解密失败：code->{}", code);
            return "";
        }
    }


    /**
     * 初始化微信支付配置
     */
    /*@PostConstruct
    public void init() {
        // RSAAutoCertificateConfig对象为自动更新微信平台证书的对象，可发起微信支付调用，也可以接受通知进行解析
        notificationConfig = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKeyFromPath(privateKeyPath)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();
    }*/

}

